﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MentalAlchemy.Atomics;
using MentalAlchemy.Molecules.NevaAlgorithm.Compounds;
using MentalAlchemy.Molecules.NevaAlgorithm.nevamod;

namespace OpsAnalyzer
{
	public partial class MainForm : Form
	{
		OperatorsAnalyzerControl opsAnalyzer = new OperatorsAnalyzerControl();

		public MainForm()
		{
			InitializeComponent();

			panel1.Controls.Add(opsAnalyzer);
			opsAnalyzer.Dock = DockStyle.Fill;
		}

		private void LoadBtn_Click(object sender, EventArgs e)
		{
			var res = openFileDialog1.ShowDialog();
			if (res != DialogResult.OK) { return; }

			var filename = openFileDialog1.FileName;
			var lines = File.ReadAllLines(filename);

			// look for the special string.
			var tStr = "> The best solution";
			int idx = 0;
			for (int i = 0; i < lines.Length; i++, idx++)
			{
				if (lines[i].StartsWith(tStr))
				{
					break;
				}
			}
			// if no special line is met.
			if (idx == lines.Length) { return; }

			// read changes.
			// "+1" to skip the special line.
			var chs = new List<NevaIndChanges>();
			var opsAn = new OperatorsAnalyzer();
			for (int i = idx+1; i < lines.Length; i++)
			{
				if (lines[i].Length == 0) { break; }

				var ch = NevaIndChanges.FromString(lines[i]);
				chs.Add(ch);
			}
			opsAn.Changes = chs;
			opsAnalyzer.OpsAnalyzer = opsAn;

			// test. Calc trans M.
			var transDict = OperatorsAnalyzer.ComputeSuccessfulTransitions(chs, true);
			var transM = OperatorsAnalyzer.TransitionsToMatrix(transDict, Operators.GetMutOperators());
		}

		private void LoadMultBtn_Click(object sender, EventArgs e)
		{
			// load changes for multiple runs.
			var res = openFileDialog1.ShowDialog();
			if (res != DialogResult.OK) { return; }

			var filename = openFileDialog1.FileName;
			var chs = LoadChanges(filename);

			//
			// analyse changes.
			var ops = Operators.GetMutOperators();

			#region - 0. Get generations data. -
			var genData = new Dictionary<string, List<int>>();
			for (int i = 0; i < ops.Length; i++) { genData.Add(ops[i], new List<int>()); }
			for (int i = 0; i < chs.Count; i++)
			{
				var gens = OperatorsAnalyzer.GetGenerationsData(chs[i]);

				foreach (var key in genData.Keys)
				{
					if (gens.ContainsKey(key))
					{
						genData[key].AddRange(gens[key]);
					}
				}
			}
			#endregion

			#region - 1. Get ops utilities. -
			var utilData = new Dictionary<string, List<float>> ();
			for (int i = 0; i < ops.Length; i++) { utilData.Add(ops[i], new List<float>()); }
			foreach (var ch in chs)
			{
				var splChs = OperatorsAnalyzer.SplitByOperations(ch);
				foreach (var opChs in splChs)
				{
					var deltas = OperatorsAnalyzer.GetFitnessDeltaValues(opChs.Value);
					if (utilData.ContainsKey(opChs.Key))
					{
						utilData[opChs.Key].AddRange(deltas);
					}
				}
			}
			var utilStats = new Dictionary<string, Stats>();
			foreach (var pair in utilData)
			{
				if (pair.Value.Count > 0)
				{
					utilStats.Add(pair.Key, VectorMath.CalculateStats(pair.Value.ToArray()));
				}
				else
				{
					utilStats.Add(pair.Key, new Stats());
				}
			}
			#endregion

			//
			// write log.
			const char SEP = '\t';
			var lines = new List<string>();

			// write generation data.
			var data = new List<int[]>();
			var header = "";
			foreach (var pair in genData)
			{
				data.Add(pair.Value.ToArray());
				header += pair.Key + SEP;
			}
			lines.Add("> Operators generations:");
			lines.Add(header);
			// transpose data matrix by hands.
			// find max row length.
			var size = data[0].Length;
			for (int i = 1; i < data.Count; i++) { if (size < data[i].Length) {size = data[i].Length; } }
			for (int i = 0; i < size; i++)
			{
				var str = "";
				for (int j = 0; j < data.Count; j++)
				{
					if (data[j].Length > i)
					{
						str += data[j][i];
					}
					str += SEP;
				}
				lines.Add(str);
			}

			// write utilities stats.
			lines.Add("\n> Operators utilities stats:");
			foreach (var stat in utilStats)
			{
				lines.Add(stat.Key + SEP + stat.Value.GetStatsString());
			}

			// get log filename.
			var logfile = Path.GetFileName(filename);
			var idx = logfile.IndexOf('_');
			logfile = logfile.Substring(0, idx) + ".log";
			File.WriteAllLines(logfile, lines.ToArray());
		}

		private List<List<NevaIndChanges>> LoadChanges (string file)
		{
			var lines = File.ReadAllLines(file);
			var res = new List<List<NevaIndChanges>>();

			// look for the special string.
			var tStr = "> The best solution #";
			int idx = 0;
			do
			{
				for (int i = idx; i < lines.Length; i++, idx++)
				{
					if (lines[i].StartsWith(tStr))
					{
						break;
					}
				}

				if (idx < lines.Length)
				{	// read changes data.
					// read changes.
					// "+2" to skip the special line and struct data.
					var chs = new List<NevaIndChanges>();
					for (int i = idx + 2; i < lines.Length; i++)
					{
						if (lines[i].Length == 0) { break; }

						var ch = NevaIndChanges.FromString(lines[i]);
						chs.Add(ch);
					}
					res.Add(chs);
					idx += chs.Count;	// skip lines with changes info.
				}
				else {break;}
			} while (idx < lines.Length);

			return res;
		}
	}
}
